package org.sraosha.microservice.oauth2.controller;

import org.sraosha.framework.constants.RedisStorage;
import org.sraosha.framework.dto.ApiResult;
import org.sraosha.framework.dto.ServiceResult;
import org.sraosha.framework.dto.VerificationCode;
import org.sraosha.framework.enumeration.ApiResultEnum;
import org.sraosha.framework.util.ImgUtil;
import org.sraosha.framework.util.StringUtils;
import org.sraosha.microservice.oauth2.model.User;
import org.sraosha.microservice.oauth2.constants.RedisKey;
import org.sraosha.microservice.oauth2.pojo.UserInfo;
import org.sraosha.microservice.oauth2.vo.UserVo;
import org.sraosha.microservice.oauth2.vo.VerificationCodeVo;
import com.alibaba.fastjson.JSON;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.oauth2.common.OAuth2AccessToken;
import org.springframework.security.oauth2.common.exceptions.InvalidTokenException;
import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.security.Principal;
import java.util.Date;
import java.util.Map;

/**
 * 自定义/oauth/token、/oauth/check_token接口
 */
@Api(value = "自定义认证接口的controller类", tags = "OauthController")
@RestController
@RequestMapping("/oauth")
@Slf4j
public class OauthController extends AbstractController implements InitializingBean {

    @ApiOperation("获取验证码。两个数据计算")
    @RequestMapping(value = "/verificationCode", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult<VerificationCode>> verificationCode(HttpServletResponse response) throws IOException {

        // 生成验证图片
        VerificationCode verificationCode = ImgUtil.createVerificationCode();

        // 将图片验证码的uuid和计算结果保存到redis
        String uuid = verificationCode.getUuid();
        StringBuffer key = new StringBuffer().append(RedisKey.SYSTEM_NAME).append(RedisStorage.COLON).append(RedisKey.SERVICE_NAME)
                .append(RedisStorage.COLON).append(RedisKey.VERIFICATION_CODE).append(RedisStorage.COLON)
                .append(uuid);
        redisManager.set(key.toString(), verificationCode.getCalculationResult().toString(), oauthValue.getVerificationCodeTimeout());
//        redisManager.set(key.toString(), verificationCode.getCalculationResult().toString(), 7200);

        ApiResult<VerificationCode> apiResult = new ApiResult();
        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        verificationCode.setCalculationResult(null);
        apiResult.setResult(verificationCode);
        return new ResponseEntity<ApiResult<VerificationCode>>(apiResult, HttpStatus.OK);
    }

    @ApiOperation("用户注册。username不能重复，password和confirmPassword必须相同")
    @ApiImplicitParam(name = "userVo", value = "User的vo类", required = true, dataType = "UserVo")
    @RequestMapping(value = "/register", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult<User>> register(@RequestBody UserVo userVo) {
        ApiResult apiResult = new ApiResult();
        String message;
        if (null == userVo || StringUtils.isEmpty(userVo.getUsername()) || StringUtils.isEmpty(userVo.getPassword())
                || StringUtils.isEmpty(userVo.getConfirmPassword())) {
            message = "调用接口/oauth/register时，参数username、password、confirmPassword不能为空";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(ApiResultEnum.PARAMETER_EMPTY.getCode());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<User>>(apiResult, HttpStatus.BAD_REQUEST);
        }
        if (!userVo.getPassword().equals(userVo.getConfirmPassword())) {
            message = "调用接口/oauth/register时，参数password和confirmPassword不相同";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(ApiResultEnum.PASSWORD_NOT_SAME.getCode());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<User>>(apiResult, HttpStatus.BAD_REQUEST);
        }

        User user = new User();
        BeanUtils.copyProperties(userVo, user);
        user.setCreateTime(new Date());
        user.setUpdateTime(new Date());
        user.setPassword(new BCryptPasswordEncoder().encode(user.getPassword()));
        ServiceResult<User> userServiceResult = userService.addWithoutDuplicateUsername(user);

        apiResult.setSuccess(userServiceResult.getSuccess());
        apiResult.setResult(userServiceResult.getResult());
        if (false == userServiceResult.getSuccess()){
            apiResult.setMessage(ApiResultEnum.USERNAME_DUPLICATE.getMessage());
            apiResult.setCode(ApiResultEnum.USERNAME_DUPLICATE.getCode());
        } else {
            userServiceResult.getResult().setPassword(null);
            apiResult.setMessage(userServiceResult.getMessage());
            apiResult.setCode(userServiceResult.getCode());
        }
        return new ResponseEntity<ApiResult<User>>(apiResult, HttpStatus.OK);
    }

    @ApiOperation("重写/oauth/token这个默认接口，返回的数据格式统一")
    @ApiImplicitParams({@ApiImplicitParam(name = "principal", value = "principal", required = true, dataType = "Principal"),
            @ApiImplicitParam(name = "parameters", value = "参数Map", required = true, dataType = "Map<String, String>")})
    @RequestMapping(value = "/token", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult<OAuth2AccessToken>> postAccessToken(Principal principal, @RequestParam Map<String, String> parameters,
                                                                        @RequestBody VerificationCodeVo verificationCodeVo) throws HttpRequestMethodNotSupportedException {
        ApiResult apiResult = new ApiResult();
        if (null == parameters) {
            String message = "调用接口【/oauth/token时，参数不能为空";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<OAuth2AccessToken>>(apiResult, HttpStatus.BAD_REQUEST);
        }
        if (null == verificationCodeVo || null == verificationCodeVo.getCalculationResult()
                || StringUtils.isEmpty(verificationCodeVo.getImageUUID())) {
            String message = "调用接口【/oauth/token时，参数verificationCodeVo、calculationResult、imageUUID不能为空";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<OAuth2AccessToken>>(apiResult, HttpStatus.BAD_REQUEST);
        }

        // 验证图片验证码
        Integer imageCode = verificationCodeVo.getCalculationResult();
        String imageUUID = verificationCodeVo.getImageUUID();
        StringBuffer key = new StringBuffer().append(RedisKey.SYSTEM_NAME).append(RedisStorage.COLON).append(RedisKey.SERVICE_NAME)
                .append(RedisStorage.COLON).append(RedisKey.VERIFICATION_CODE).append(RedisStorage.COLON)
                .append(imageUUID);
        String calResult;
        try {
            calResult = (String) redisManager.get(key.toString());
        } catch (Exception e){
            e.printStackTrace();

            String message = "调用接口【/oauth/token时，图片验证码过期";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<OAuth2AccessToken>>(apiResult, HttpStatus.BAD_REQUEST);
        }
        if (StringUtils.isEmpty(calResult)){
            String message = "调用接口【/oauth/token时，图片验证码过期";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<OAuth2AccessToken>>(apiResult, HttpStatus.BAD_REQUEST);
        }
        Integer calculationResult = Integer.valueOf(calResult);
        if (!imageCode.equals(calculationResult)){
            String message = "调用接口【/oauth/token时，参数image_code错误";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<OAuth2AccessToken>>(apiResult, HttpStatus.BAD_REQUEST);
        }

        OAuth2AccessToken accessToken = tokenEndpoint.postAccessToken(principal, parameters).getBody();

        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setResult(accessToken);
        return new ResponseEntity<ApiResult<OAuth2AccessToken>>(apiResult, HttpStatus.OK);
    }

    @ApiOperation("重写/oauth/check_token这个默认接口，用于校验令牌，返回的数据格式统一")
    @ApiImplicitParam(name = "token", value = "token", required = true, dataType = "String")
    @RequestMapping(value = "/check_token", method = RequestMethod.POST, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult<Map<String, ?>>> checkToken(@RequestParam("token") String token) {
        ApiResult apiResult = new ApiResult();
        String message;
        if (StringUtils.isEmpty(token)) {
            message = "调用接口【/oauth/check_token时，参数token不能为空";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<Map<String, ?>>>(apiResult, HttpStatus.BAD_REQUEST);
        }

        // 验证token
        Map<String, ?> map;
        try {
            map = checkTokenEndpoint.checkToken(token);
        } catch (InvalidTokenException e) {
            e.printStackTrace();

            message = "token：【" + token + "】无法被识别";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(HttpStatus.BAD_REQUEST.value());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult<Map<String, ?>>>(apiResult, HttpStatus.BAD_REQUEST);
        }
        message = "token：【" + token + "】验证通过";
        log.info(message);

        UserInfo userInfo = userInfoService.findByUsername((String) map.get("user_name"));
        userInfo.setClientId((String) map.get("client_id"));

        // 将token和用户信息存储到redis
        StringBuffer key = new StringBuffer().append(RedisKey.SYSTEM_NAME).append(RedisStorage.COLON).append(RedisKey.SERVICE_NAME)
                .append(RedisStorage.COLON).append(RedisStorage.TOKEN_PREFIX)
                .append(token);
        redisManager.set(key.toString(), JSON.toJSONString(userInfo), oauthValue.getLoginTimeout());
//        redisManager.set(key.toString(), JSON.toJSONString(userInfo), 180);

        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setResult(userInfo);
        apiResult.setMessage(message);
        return new ResponseEntity<ApiResult<Map<String, ?>>>(apiResult, HttpStatus.OK);
    }

    @ApiOperation("退出登录")
    @ApiImplicitParam(name = "username", value = "用户名", required = true, dataType = "String")
    @RequestMapping(value = "/logout/{username}", method = RequestMethod.GET, produces = {"application/json; charset=utf-8"})
    @ResponseBody
    public ResponseEntity<ApiResult> logout(@PathVariable String username) {
        ApiResult apiResult = new ApiResult();
        String message;
        if (StringUtils.isEmpty(username)) {
            message = "调用接口【/oauth/logout/{username}，参数username不能为空";
            log.warn(message);
            apiResult.setSuccess(Boolean.FALSE);
            apiResult.setCode(ApiResultEnum.PARAMETER_EMPTY.getCode());
            apiResult.setMessage(message);
            return new ResponseEntity<ApiResult>(apiResult, HttpStatus.BAD_REQUEST);
        }

        // 从redis删除用户信息
        StringBuffer key = new StringBuffer().append(RedisKey.SYSTEM_NAME).append(RedisStorage.COLON).append(RedisKey.SERVICE_NAME)
                .append(RedisStorage.COLON).append(RedisStorage.TOKEN_PREFIX)
                .append(username);
        redisManager.del(key.toString());

        apiResult.setSuccess(Boolean.TRUE);
        apiResult.setCode(HttpStatus.OK.value());
        apiResult.setMessage(ApiResultEnum.LOGOUT_SUCCESS.getMessage());
        return new ResponseEntity<ApiResult>(apiResult, HttpStatus.OK);
    }

    @Override
    public void afterPropertiesSet() throws Exception {

    }
}
